/*     */ package com.zimbra.cs.account.ldap;
/*     */ 
/*     */ import com.zimbra.common.account.Key.CosBy;
/*     */ import com.zimbra.common.account.Key.DomainBy;
/*     */ import com.zimbra.common.service.ServiceException;
/*     */ import com.zimbra.common.util.Log;
/*     */ import com.zimbra.common.util.ZimbraLog;
/*     */ import com.zimbra.cs.account.Account;
/*     */ import com.zimbra.cs.account.AccountServiceException;
/*     */ import com.zimbra.cs.account.AttributeClass;
/*     */ import com.zimbra.cs.account.Cos;
/*     */ import com.zimbra.cs.account.Domain;
/*     */ import com.zimbra.cs.account.Provisioning;
/*     */ import com.zimbra.cs.account.Provisioning.ProvisioningValidator;
/*     */ import com.zimbra.cs.ldap.IAttributes;
/*     */ import com.zimbra.cs.ldap.IAttributes.CheckBinary;
/*     */ import com.zimbra.cs.ldap.SearchLdapOptions.SearchLdapVisitor;
/*     */ import com.zimbra.cs.ldap.ZLdapFilter;
/*     */ import com.zimbra.cs.ldap.ZLdapFilterFactory;
/*     */ import com.zimbra.soap.admin.type.CountObjectsType;
/*     */ import java.util.Arrays;
/*     */ import java.util.HashMap;
/*     */ import java.util.HashSet;
/*     */ import java.util.Iterator;
/*     */ import java.util.List;
/*     */ import java.util.Map;
/*     */ import java.util.Map.Entry;
/*     */ import java.util.Set;
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ public final class Validators
/*     */ {
/*     */   public static class DomainAccountValidator
/*     */     implements Provisioning.ProvisioningValidator
/*     */   {
/*     */     private static final long LDAP_CHECK_INTERVAL = 60000L;
/*     */     private static final long NUM_ACCT_THRESHOLD = 5L;
/*     */     private long mNextCheck;
/*     */     private long mLastUserCount;
/*     */     
/*     */     public void refresh()
/*     */     {
/*  57 */       setNextCheck(0L);
/*     */     }
/*     */     
/*     */     private synchronized void setNextCheck(long nextCheck) {
/*  61 */       this.mNextCheck = nextCheck;
/*     */     }
/*     */     
/*     */     private synchronized long getNextCheck() {
/*  65 */       return this.mNextCheck;
/*     */     }
/*     */     
/*     */     public void validate(Provisioning prov, String action, Object... args) throws ServiceException
/*     */     {
/*  70 */       if (args.length < 1) return;
/*  71 */       if (((!action.equals("createAccount")) && (!action.equals("renameAccount"))) || (!(args[0] instanceof String))) {
/*  72 */         return;
/*     */       }
/*  74 */       if ((args.length > 1) && ((args[1] instanceof String[])) && (Arrays.asList((String[])args[1]).contains(AttributeClass.OC_zimbraCalendarResource)))
/*     */       {
/*  76 */         return;
/*     */       }
/*     */       
/*  79 */       if ((args.length > 2) && ((args[2] instanceof Map)))
/*     */       {
/*  81 */         Map<String, Object> acctAttrs = (Map)args[2];
/*  82 */         if (Validators.isSystemProperty(acctAttrs)) {
/*  83 */           return;
/*     */         }
/*  85 */         if (Validators.isExternalVirtualAccount(acctAttrs)) {
/*  86 */           return;
/*     */         }
/*     */       }
/*     */       
/*  90 */       String emailAddr = (String)args[0];
/*  91 */       String domain = null;
/*  92 */       int index = emailAddr.indexOf('@');
/*  93 */       if (index != -1) {
/*  94 */         domain = emailAddr.substring(index + 1);
/*     */       }
/*  96 */       if (domain == null) {
/*  97 */         return;
/*     */       }
/*  99 */       Domain d = prov.get(Key.DomainBy.name, domain);
/* 100 */       if (d == null) {
/* 101 */         return;
/*     */       }
/* 103 */       String limit = d.getAttr("zimbraDomainMaxAccounts");
/* 104 */       if (limit == null) {
/* 105 */         return;
/*     */       }
/* 107 */       long maxAccount = Long.parseLong(limit);
/* 108 */       long now = System.currentTimeMillis();
/* 109 */       if (now > getNextCheck()) {
/*     */         try {
/* 111 */           this.mLastUserCount = prov.countObjects(CountObjectsType.internalUserAccount, d, null);
/*     */         } catch (ServiceException e) {
/* 113 */           Throwable cause = e.getCause();
/* 114 */           String causeMsg = cause.getMessage();
/*     */           
/*     */ 
/* 117 */           if ((causeMsg != null) && (causeMsg.contains("timeout"))) {
/* 118 */             throw ServiceException.FAILURE("The directory may not be responding or is responding slowly.  The directory may need tuning or the LDAP read timeout may need to be raised.  Otherwise, removing the zimbraDomainMaxAccounts restriction will avoid this check.", e);
/*     */           }
/*     */           
/*     */ 
/* 122 */           throw ServiceException.FAILURE("Unable to count users for setting zimbraDomainMaxAccounts=" + limit + "" + " in domain " + d.getName(), e);
/*     */         }
/*     */         
/*     */ 
/* 126 */         long nextCheck = maxAccount - this.mLastUserCount > 5L ? 60000L : 0L;
/*     */         
/* 128 */         setNextCheck(nextCheck);
/*     */       }
/*     */       
/* 131 */       if (maxAccount <= this.mLastUserCount) {
/* 132 */         throw AccountServiceException.TOO_MANY_ACCOUNTS("domain=" + domain + " (" + maxAccount + ")");
/*     */       }
/*     */     }
/*     */   }
/*     */   
/*     */   private static boolean isSystemProperty(Map<String, Object> attrs) {
/* 138 */     if (attrs == null) {
/* 139 */       return false;
/*     */     }
/* 141 */     Object o = attrs.get("zimbraIsSystemResource");
/* 142 */     if ((o != null) && ("true".equalsIgnoreCase(o.toString()))) {
/* 143 */       return true;
/*     */     }
/*     */     
/*     */ 
/*     */ 
/* 148 */     o = attrs.get("objectClass");
/* 149 */     if ((o instanceof String[])) {
/* 150 */       Set<String> ocs = new HashSet(Arrays.asList((String[])o));
/* 151 */       if (ocs.contains(AttributeClass.OC_zimbraCalendarResource)) {
/* 152 */         return true;
/*     */       }
/*     */     }
/* 155 */     return false;
/*     */   }
/*     */   
/*     */   private static boolean isExternalVirtualAccount(Map<String, Object> attrs) {
/* 159 */     if (attrs == null) {
/* 160 */       return false;
/*     */     }
/* 162 */     Object o = attrs.get("zimbraIsExternalVirtualAccount");
/* 163 */     return (o != null) && ("true".equalsIgnoreCase(o.toString()));
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */   public static class DomainMaxAccountsValidator
/*     */     implements Provisioning.ProvisioningValidator
/*     */   {
/*     */     public void refresh() {}
/*     */     
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */     public void validate(Provisioning prov, String action, Object... args)
/*     */       throws ServiceException
/*     */     {
/* 184 */       if ((!"createAccountCheckDomainCosAndFeature".equals(action)) && (!"renameAccountCheckDomainCosAndFeature".equals(action)) && (!"modifyAccountCheckDomainCosAndFeature".equals(action)))
/*     */       {
/*     */ 
/* 187 */         return;
/*     */       }
/*     */       
/* 190 */       if (args.length < 2) {
/* 191 */         return;
/*     */       }
/* 193 */       HashMap<String, Integer> cosCountMap = new HashMap();
/* 194 */       HashMap<String, Integer> cosLimitMap = new HashMap();
/* 195 */       HashMap<String, Integer> featureCountMap = new HashMap();
/* 196 */       HashMap<String, Integer> featureLimitMap = new HashMap();
/* 197 */       HashMap<String, Set<String>> cosFeatureMap = new HashMap();
/*     */       
/* 199 */       String emailAddress = (String)args[0];
/* 200 */       if (emailAddress == null) {
/* 201 */         return;
/*     */       }
/*     */       
/* 204 */       Map<String, Object> attrs = (Map)args[1];
/* 205 */       if (Validators.isSystemProperty(attrs)) {
/* 206 */         return;
/*     */       }
/* 208 */       if (Validators.isExternalVirtualAccount(attrs)) {
/* 209 */         return;
/*     */       }
/*     */       
/* 212 */       Account account = null;
/* 213 */       if (args.length == 3)
/* 214 */         account = (Account)args[2];
/* 215 */       String domainName = null;
/* 216 */       int index = emailAddress.indexOf('@');
/* 217 */       if (index != -1) {
/* 218 */         domainName = emailAddress.substring(index + 1);
/*     */       }
/* 220 */       if (domainName == null) {
/* 221 */         return;
/*     */       }
/* 223 */       Domain domain = prov.get(Key.DomainBy.name, domainName);
/* 224 */       if (domain == null) {
/* 225 */         return;
/*     */       }
/* 227 */       String defaultCosId = domain.getAttr("zimbraDomainDefaultCOSId");
/* 228 */       if (defaultCosId == null) {
/* 229 */         Cos defaultCos = prov.get(Key.CosBy.name, "default");
/* 230 */         if (defaultCos != null) {
/* 231 */           defaultCosId = defaultCos.getId();
/*     */         }
/*     */       }
/* 234 */       Set<String> cosLimit = domain.getMultiAttrSet("zimbraDomainCOSMaxAccounts");
/*     */       
/* 236 */       Set<String> featureLimit = domain.getMultiAttrSet("zimbraDomainFeatureMaxAccounts");
/*     */       
/*     */ 
/* 239 */       if ((cosLimit.size() == 0) && (featureLimit.size() == 0)) {
/* 240 */         return;
/*     */       }
/* 242 */       for (String limit : cosLimit)
/* 243 */         parseLimit(cosLimitMap, limit);
/* 244 */       for (String limit : featureLimit) {
/* 245 */         parseLimit(featureLimitMap, limit);
/*     */       }
/*     */       
/* 248 */       for (Map.Entry<String, Integer> e : cosLimitMap.entrySet())
/* 249 */         cosCountMap.put(e.getKey(), Integer.valueOf(0));
/* 250 */       for (Map.Entry<String, Integer> e : featureLimitMap.entrySet()) {
/* 251 */         featureCountMap.put(e.getKey(), Integer.valueOf(0));
/*     */       }
/* 253 */       boolean isModifyingCosId = (attrs != null) && (attrs.get("zimbraCOSId") != null);
/* 254 */       boolean isCreatingEntry = "createAccountCheckDomainCosAndFeature".equals(action);
/*     */       
/* 256 */       String desiredCosId = null;
/*     */       
/* 258 */       if ((isModifyingCosId) || (isCreatingEntry)) {
/* 259 */         if (attrs != null) {
/* 260 */           desiredCosId = (String)attrs.get("zimbraCOSId");
/*     */         }
/* 262 */         if (desiredCosId == null) {
/* 263 */           desiredCosId = defaultCosId;
/*     */         }
/*     */         
/*     */ 
/*     */       }
/* 268 */       else if (account != null) {
/* 269 */         desiredCosId = account.getCOS().getId();
/*     */       }
/*     */       else
/*     */       {
/* 273 */         throw ServiceException.FAILURE("account object is null", null);
/*     */       }
/*     */       
/*     */ 
/* 277 */       Set<String> cosFeatures = getCosFeatures(prov, cosFeatureMap, desiredCosId, defaultCosId);
/* 278 */       Set<String> desiredFeatures = new HashSet();
/*     */       
/* 280 */       if (attrs != null) {
/* 281 */         for (Map.Entry<String, Object> entry : attrs.entrySet()) {
/* 282 */           String k = (String)entry.getKey();
/* 283 */           if ((featureLimitMap.containsKey(k)) && ("true".equalsIgnoreCase(entry.getValue().toString())))
/*     */           {
/* 285 */             desiredFeatures.add(k);
/*     */           }
/*     */         }
/*     */       }
/*     */       
/* 290 */       if (cosFeatures != null) {
/* 291 */         for (String feature : cosFeatures) {
/* 292 */           if (featureLimitMap.containsKey(feature))
/* 293 */             desiredFeatures.add(feature);
/*     */         }
/*     */       }
/* 296 */       if (ZimbraLog.account.isDebugEnabled())
/* 297 */         ZimbraLog.account.debug("Desired features (incl. cos): %s + %s", new Object[] { desiredFeatures, cosFeatures });
/* 298 */       String originalCosId = null;
/*     */       
/* 300 */       if (account != null) {
/* 301 */         originalCosId = account.getAttr("zimbraCOSId");
/*     */         
/*     */ 
/* 304 */         if (originalCosId == null)
/* 305 */           originalCosId = defaultCosId;
/* 306 */         Set<String> features = getCosFeatures(prov, cosFeatureMap, originalCosId, defaultCosId);
/* 307 */         if (features != null)
/* 308 */           desiredFeatures.removeAll(features); }
/*     */       Map<String, Object> acctAttrs;
/*     */       Iterator<String> i;
/* 311 */       if ((desiredFeatures.size() > 0) && 
/* 312 */         (account != null)) {
/* 313 */         acctAttrs = account.getAttrs(false);
/* 314 */         for (i = desiredFeatures.iterator(); i.hasNext();) {
/* 315 */           String feature = (String)i.next();
/* 316 */           if ((acctAttrs.containsKey(feature)) && ("true".equalsIgnoreCase(acctAttrs.get(feature).toString())))
/*     */           {
/* 318 */             i.remove();
/*     */           }
/*     */         }
/*     */       }
/* 322 */       if (((desiredCosId != null) && (!desiredCosId.equals(originalCosId)) && (cosLimitMap.containsKey(desiredCosId))) || (desiredFeatures.size() > 0))
/*     */       {
/* 324 */         if (ZimbraLog.account.isDebugEnabled()) {
/* 325 */           ZimbraLog.account.debug("COS change info [%s:%s], desired features %s", new Object[] { originalCosId, desiredCosId, desiredFeatures });
/*     */         }
/*     */         
/*     */ 
/* 329 */         buildDomainCounts(prov, domainName, defaultCosId, cosCountMap, featureCountMap, cosFeatureMap);
/*     */         
/* 331 */         if (ZimbraLog.account.isDebugEnabled())
/* 332 */           ZimbraLog.account.debug("COS/Feature limits: %s + %s", new Object[] { cosLimitMap, featureLimitMap });
/* 333 */         if ((desiredCosId != null) && (!desiredCosId.equals(originalCosId)) && (cosLimitMap.containsKey(desiredCosId)))
/*     */         {
/* 335 */           if ((cosCountMap.containsKey(desiredCosId)) && (((Integer)cosCountMap.get(desiredCosId)).intValue() >= ((Integer)cosLimitMap.get(desiredCosId)).intValue()))
/*     */           {
/* 337 */             throw AccountServiceException.TOO_MANY_ACCOUNTS(String.format("domain=%s[cos=%s,count=%d,limit=%d]", new Object[] { domainName, desiredCosId, cosCountMap.get(desiredCosId), cosLimitMap.get(desiredCosId) }));
/*     */           }
/*     */         }
/*     */         
/*     */ 
/* 342 */         if (desiredFeatures.size() > 0) {
/* 343 */           for (String feature : desiredFeatures) {
/* 344 */             if ((featureCountMap.containsKey(feature)) && (((Integer)featureCountMap.get(feature)).intValue() >= ((Integer)featureLimitMap.get(feature)).intValue()))
/*     */             {
/* 346 */               throw AccountServiceException.TOO_MANY_ACCOUNTS(String.format("domain=%s[%s,count=%d,limit=%d]", new Object[] { domainName, feature, featureCountMap.get(feature), featureLimitMap.get(feature) }));
/*     */             }
/*     */           }
/*     */         }
/*     */       }
/*     */     }
/*     */     
/*     */ 
/*     */ 
/*     */     private static Set<String> getCosFeatures(Provisioning prov, Map<String, Set<String>> cosFeatureMap, String cosId, String defaultCosId)
/*     */       throws ServiceException
/*     */     {
/* 358 */       if (!cosFeatureMap.containsKey(cosId)) {
/* 359 */         Cos cos = null;
/* 360 */         if (cosId != null)
/* 361 */           cos = prov.get(Key.CosBy.id, cosId);
/* 362 */         if (cos == null) {
/* 363 */           if (defaultCosId != null) {
/* 364 */             ZimbraLog.account.debug("COS id %s not found, reverting to %s", new Object[] { cosId, defaultCosId });
/* 365 */             return getCosFeatures(prov, cosFeatureMap, defaultCosId, null);
/*     */           }
/*     */           
/* 368 */           ZimbraLog.account.debug("COS %s not found, bailing!", new Object[] { cosId });
/* 369 */           return null;
/*     */         }
/*     */         
/* 372 */         Map<String, Object> cosAttrs = cos.getAttrs(true);
/* 373 */         Set<String> features = new HashSet();
/* 374 */         for (Map.Entry<String, Object> entry : cosAttrs.entrySet()) {
/* 375 */           String name = (String)entry.getKey();
/* 376 */           if ((name.toLowerCase().startsWith("zimbrafeature")) && (name.toLowerCase().endsWith("enabled")))
/*     */           {
/* 378 */             Object value = entry.getValue();
/* 379 */             if ((value != null) && ("true".equalsIgnoreCase(value.toString())))
/* 380 */               features.add(name);
/*     */           }
/*     */         }
/* 383 */         cosFeatureMap.put(cosId, features);
/*     */       }
/* 385 */       return (Set)cosFeatureMap.get(cosId);
/*     */     }
/*     */     
/* 388 */     private static void parseLimit(HashMap<String, Integer> map, String limit) { String[] parts = limit.split(":");
/* 389 */       int max = -1;
/*     */       try {
/* 391 */         max = Integer.parseInt(parts[1]);
/*     */       }
/*     */       catch (NumberFormatException e) {}
/*     */       
/*     */ 
/* 396 */       if (max < 0)
/* 397 */         return;
/* 398 */       map.put(parts[0], Integer.valueOf(max));
/*     */     }
/*     */     
/*     */     private static class BuildDomainCounts
/*     */       extends SearchLdapOptions.SearchLdapVisitor
/*     */     {
/*     */       private Provisioning prov;
/*     */       private String domain;
/*     */       private String defaultCos;
/*     */       private Map<String, Integer> cosCount;
/*     */       private Map<String, Integer> featureCount;
/*     */       private Map<String, Set<String>> cosFeatureMap;
/*     */       
/*     */       private BuildDomainCounts(Provisioning prov, String domain, String defaultCos, Map<String, Integer> cosCount, Map<String, Integer> featureCount, Map<String, Set<String>> cosFeatureMap)
/*     */       {
/* 413 */         this.prov = prov;
/* 414 */         this.domain = domain;
/* 415 */         this.defaultCos = defaultCos;
/* 416 */         this.cosCount = cosCount;
/* 417 */         this.featureCount = featureCount;
/* 418 */         this.cosFeatureMap = cosFeatureMap;
/*     */       }
/*     */       
/*     */       void search() throws ServiceException {
/* 422 */         LdapProv ldapProv = (LdapProv)this.prov;
/* 423 */         String searchBaseDN = ldapProv.getDIT().domainToAccountSearchDN(this.domain);
/* 424 */         ZLdapFilter query = ZLdapFilterFactory.getInstance().allNonSystemAccounts();
/*     */         
/* 426 */         ldapProv.searchLdapOnReplica(searchBaseDN, query, null, this);
/* 427 */         ZimbraLog.account.debug("COS/Feature counts: %s + %s", new Object[] { this.cosCount, this.featureCount });
/*     */       }
/*     */       
/*     */       public void visit(String dn, Map<String, Object> attrs, IAttributes ldapAttrs)
/*     */       {
/*     */         try {
/* 433 */           visitInternal(dn, attrs, ldapAttrs);
/*     */         } catch (ServiceException e) {
/* 435 */           ZimbraLog.account.error("encountered error, entry skipped ", e);
/*     */         }
/*     */       }
/*     */       
/*     */       private void visitInternal(String dn, Map<String, Object> attrs, IAttributes ldapAttrs) throws ServiceException
/*     */       {
/* 441 */         List<String> objectclass = ldapAttrs.getMultiAttrStringAsList("objectClass", IAttributes.CheckBinary.NOCHECK);
/*     */         
/* 443 */         if ((objectclass == null) || (objectclass.size() == 0)) {
/* 444 */           ZimbraLog.account.error("DN: " + dn + ": does not have objectclass!");
/* 445 */           return;
/*     */         }
/*     */         
/* 448 */         if (objectclass.contains(AttributeClass.OC_zimbraAccount)) {
/* 449 */           String cosId = ldapAttrs.getAttrString("zimbraCOSId");
/* 450 */           if (cosId == null) {
/* 451 */             cosId = this.defaultCos;
/*     */           }
/*     */           
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/* 458 */           Validators.DomainMaxAccountsValidator.incrementCount(this.cosCount, cosId);
/* 459 */           Set<String> cosFeatures = Validators.DomainMaxAccountsValidator.getCosFeatures(this.prov, this.cosFeatureMap, cosId, this.defaultCos);
/*     */           
/* 461 */           Set<String> acctFeatures = new HashSet();
/* 462 */           for (Map.Entry<String, Object> attr : attrs.entrySet()) {
/* 463 */             String attrName = (String)attr.getKey();
/* 464 */             Object attrValue = attr.getValue();
/*     */             
/* 466 */             String value = null;
/* 467 */             if ((attrValue instanceof String)) {
/* 468 */               value = (String)attrValue;
/*     */             }
/*     */             
/* 471 */             if ((attrName.toLowerCase().startsWith("zimbrafeature")) && (attrName.toLowerCase().endsWith("enabled")) && ("true".equalsIgnoreCase(value)))
/*     */             {
/*     */ 
/* 474 */               acctFeatures.add(attrName); }
/*     */           }
/* 476 */           if (cosFeatures != null)
/* 477 */             acctFeatures.addAll(cosFeatures);
/* 478 */           for (String feature : acctFeatures)
/* 479 */             Validators.DomainMaxAccountsValidator.incrementCount(this.featureCount, feature);
/*     */         }
/*     */       }
/*     */     }
/*     */     
/*     */     private static void incrementCount(Map<String, Integer> map, String key) {
/* 485 */       if ((key == null) || (!map.containsKey(key)))
/* 486 */         return;
/* 487 */       map.put(key, Integer.valueOf(((Integer)map.get(key)).intValue() + 1));
/*     */     }
/*     */     
/*     */ 
/*     */     private void buildDomainCounts(Provisioning prov, String domain, String defaultCos, Map<String, Integer> cosCount, Map<String, Integer> featureCount, Map<String, Set<String>> cosFeatureMap)
/*     */       throws ServiceException
/*     */     {
/* 494 */       BuildDomainCounts counts = new BuildDomainCounts(prov, domain, defaultCos, cosCount, featureCount, cosFeatureMap, null);
/*     */       
/* 496 */       counts.search();
/*     */     }
/*     */   }
/*     */ }


/* Location:              /home/mint/zimbrastore.jar!/com/zimbra/cs/account/ldap/Validators.class
 * Java compiler version: 7 (51.0)
 * JD-Core Version:       0.7.1
 */